//
// GPSMapEdit
// (c) Konstantin Galichsky (kg@geopainting.com), 2002-2007
//
// Recent files handling.
//

# include "StdAfx.h"
# include "RecentFiles.h"
# include "Load.h"
# include "Scale.h"
# include "Position.h"
# include "Map.h"
# include "MapView.h"
# include "Menu.h"
# include "ParseHelpers.h"
# include "PtAlgo.h"
# include "Settings.h"
# include "Globals.h"

const size_t c_cMaxRFs = 10;

struct rf_t {
	char strName [MAX_PATH];
	point_t ptPos;
	float fScale;

	rf_t () : fScale (-1) {strName [0] = '\0';}
	
	rf_t (const char * _strName, const point_t & _ptPos, float _fScale) :
		ptPos (_ptPos),
		fScale (_fScale)
	{
		::strncpy (strName, _strName, MAX_PATH);
	}
};
typedef std::vector<rf_t> rfs_t;
static rfs_t g_rfs;

static CCritSec g_csRFS;

static
size_t _FindRF (const char * _strPath) {
	const size_t cRFSs = g_rfs.size ();
	for (size_t c = 0; c < c_cMaxRFs && c < cRFSs; ++ c) {
		if (::stricmp (g_rfs [c].strName, _strPath) == 0)
			return c;
	}

	return -1;
}

static bool g_bSeparatorAdded = false;

void UpdateRecentFilesMenu () {
	// Get 'File' menu.
	const HMENU hFileMenu = ::GetSubMenu (::GetMenu (g_hWnd), smFile);

	// Remove old recent files.
	size_t c;
	for (c = 0; c < c_cMaxRFs; ++ c)
		::RemoveMenu (hFileMenu, ID_FILE_RECENT_0 + c, MF_BYCOMMAND);

	// Add menu separator (first time only).
	if (! g_bSeparatorAdded) {
		::AppendMenu (hFileMenu, MF_SEPARATOR, 0, NULL);
		g_bSeparatorAdded = true;
	}

	// Add recent files items.
	const UINT uFlags = MF_STRING | (g_bLoading ? MF_GRAYED : MF_ENABLED);
	const size_t cRFSs = g_rfs.size ();
	for (c = 0; c < cRFSs && c < c_cMaxRFs; ++ c) {
		const size_t c_cMaxLen = 50;
		char strDisplay [c_cMaxLen + 1];
		::PathCompactPathEx (strDisplay, g_rfs [c].strName, c_cMaxLen, 0);

		char strText [MAX_PATH + 10];
		::sprintf (strText, "&%d %s", c, strDisplay);

		::AppendMenu (hFileMenu, uFlags, ID_FILE_RECENT_0 + c, strText);
	}
}

void InitRecentFiles () {
	CCritSec::CLock lock (g_csRFS);

	for (size_t c = 0; c < c_cMaxRFs; ++ c) {
		char strName [32];
		::sprintf (strName, "Recent%d", c);
		char strPos [32];
		::sprintf (strPos, "Recent%d_pos", c);

		rf_t rf;
		GetSetting (strName, rf.strName, MAX_PATH, "");
		if (rf.strName [0] == '\0')
			break;

		char strPosVal [100];
		GetSetting (strPos, strPosVal, sizeof (strPosVal)/sizeof (strPosVal [0]), "");
		rf.fScale = -1;
		if (strPosVal [0] != '\0') {
			const char * const pPosValEnd = strPosVal + ::strlen (strPosVal);
			const char * p = strPosVal;

			ParseReal (p, pPosValEnd, & p, rf.ptPos.x);
			if (rf.ptPos.x < -180 || rf.ptPos.x > 180)
				rf.ptPos.x = 0;
			if (* p == ',')
				++ p;
			ParseReal (p, pPosValEnd, & p, rf.ptPos.y);
			if (rf.ptPos.y < -90 || rf.ptPos.y > 90)
				rf.ptPos.y = 0;
			if (* p == ',')
				++ p;
			float fScale = 0;
			if (ParseReal (p, pPosValEnd, & p, fScale))
				rf.fScale = fScale;
		}

		g_rfs.push_back (rf);
	}

	if (g_rfs.empty ())
		return;

	UpdateRecentFilesMenu ();
}

static
void _SaveRFPos (size_t _cIdx) {
	CCritSec::CLock lock (g_csRFS);

	char strPos [32];
	::sprintf (strPos, "Recent%d_pos", _cIdx);
	if (g_rfs [_cIdx].fScale > 0) {
		char strPosVal [64];
		::sprintf (strPosVal, "%0.6f,%0.6f,%0.6f", g_rfs [_cIdx].ptPos.x, g_rfs [_cIdx].ptPos.y, g_rfs [_cIdx].fScale);
		SaveSetting (strPos, strPosVal);
	} else {
		SaveSetting (strPos, "");
	}
}

void AddRecentFile (const char * _strFileName) {
	CCritSec::CLock lock (g_csRFS);

	point_t ptPos;
	float fScale = -1;
	const size_t cIdx = _FindRF (_strFileName);
	if (cIdx != -1) {
		const rf_t & rf = g_rfs [cIdx];
		ptPos  = rf.ptPos;
		fScale = rf.fScale;

		// Remove existing position, if any.
		g_rfs.erase (g_rfs.begin () + cIdx);
	}

	// Insert to top.
	g_rfs.insert (g_rfs.begin (), rf_t (_strFileName, ptPos, fScale));

	// Save to registry.
	const size_t cRFSs = g_rfs.size ();
	for (size_t c = 0; c < c_cMaxRFs && c < cRFSs; ++ c) {
		char strName [32];
		::sprintf (strName, "Recent%d", c);
		SaveSetting (strName, g_rfs [c].strName);

		_SaveRFPos (c);
	}

	// Update menu.
	UpdateRecentFilesMenu ();

	::SHAddToRecentDocs (SHARD_PATH, _strFileName);
}

void LoadRecentFile (size_t _cIndex) {
	if (_cIndex >= g_rfs.size ())
		return;

	LoadMap (g_rfs [_cIndex].strName, 0, true, g_map);
}

static
bool IsDriveAccessible (const char * _strPath) {
	if (::strlen (_strPath) < 3)
		return false;

	char strRootPath [4];
	::strncpy (strRootPath, _strPath, 3);
	strRootPath [3] = '\0';

	const UINT uiType = ::GetDriveType (strRootPath);
	return uiType == DRIVE_FIXED;
}

bool IsFileAccessible (size_t _cIndex) {
	if (_cIndex >= g_rfs.size ())
		return false;

	if (! IsDriveAccessible (g_rfs [_cIndex].strName))
		return true;

	return ::GetFileAttributes (g_rfs [_cIndex].strName) != -1;
}

void SaveRecentMapPosition () {
	CCritSec::CLock lock (g_csRFS);

	if (g_strFileName [0] == '\0')
		return;
	const size_t cIdx = _FindRF (g_strFileName);
	if (cIdx == -1)
		return;

	rf_t & rf = g_rfs [cIdx];

	rect_t rectVisible;
	GetVisibleRect (rectVisible);

	rf.ptPos.x = rectVisible.MeanX ();
	rf.ptPos.y = rectVisible.MeanY ();
	rf.fScale  = g_fScale;

	_SaveRFPos (cIdx);
}

bool RestoreRecentMapPosition () {
	point_t ptPos;
	float fScale = -1;
	{
		CCritSec::CLock lock (g_csRFS);

		const size_t cIdx = _FindRF (g_strFileName);
		if (cIdx == -1)
			return false;

		const rf_t & rf = g_rfs [cIdx];
		if (rf.fScale < 0)
			return false;

		if (! g_map.rectBound.Contains (rf.ptPos))
			return false;

		ptPos  = rf.ptPos;
		fScale = rf.fScale;
	}

	SetToCenter (ptPos);
	g_fScale = -1; // NOTE: enforce SetScale() to do full job.
	SetScale (fScale, false);
	SetToCenter (ptPos); // NOTE: the second call is required to fix position.

	Refresh ();

	return true;
}